package com.heima.wemedia.service.impl;

import com.heima.common.aliyun.GreenImageUploadScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.delayTask.RedisDelayedQueueUtil;
import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.exception.LeadException;
import com.heima.common.tess4j.Tess4jClient;
import com.heima.common.util.BeanHelper;
import com.heima.common.util.JsonUtils;
import com.heima.common.util.SensitiveWordUtil;
import com.heima.feign.ApArticleFeignClient;
import com.heima.file.service.MinioService;
import com.heima.model.media.dtos.WmNewsContentItem;
import com.heima.model.media.dtos.WmNewsDelayTaskDTO;
import com.heima.model.media.dtos.WmNewsDto;
import com.heima.model.media.dtos.WmNewsResultDTO;
import com.heima.wemedia.entity.WmNews;
import com.heima.wemedia.service.WmAuditService;
import com.heima.wemedia.service.WmNewsService;
import com.heima.wemedia.service.WmSensitiveService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service("wmAuditService2")
public class WmAuditServiceImpl2 implements WmAuditService {

    @Autowired
    private WmNewsService newsService;

    @Autowired
    private ApArticleFeignClient articleFeignClient;

    @Autowired
    private GreenTextScan textScan;

    @Autowired
    private GreenImageUploadScan imageUploadScan;

    @Autowired
    private MinioService minioService;

    @Autowired
    private WmSensitiveService sensitiveService;

    @Autowired
    private Tess4jClient tess4jClient;

    @Autowired
    private RedisDelayedQueueUtil delayedQueueUtil;

    private static final String DELAY_QUEUE_NAME = "wm_news_delay_queue";

    private ThreadPoolExecutor takeTaskThreadPool = new ThreadPoolExecutor(1,1,0,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10));

    private ThreadPoolExecutor bizThreadPool = new ThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors() * 2,
            120, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            new ThreadPoolExecutor.CallerRunsPolicy());


    @Override
    @Async("taskExecutor")
    // @GlobalTransactional
    public void asyncAudit(Integer wmUserId, WmNewsDto dto) {
        log.info("开始异步审核");
        //准备要审核的数据
        List<WmNewsContentItem> wmNewsContentItems = JsonUtils.toList(dto.getContent(), WmNewsContentItem.class);
        // 要审核的文本
        String auditText = buildAuditText(wmNewsContentItems, dto);
        // 要审核的图片
        List<byte[]> auditImageBytesList = buildAuditImageBytesList(wmNewsContentItems, dto);

        log.info("阿里云文本审核：wmNewsId:{}", dto.getId());
        boolean ret = aliyunTextAudit(auditText, dto);
        if(!ret){
            return;
        }

        log.info("阿里云图片审核：wmNewsId:{}", dto.getId());
        ret = aliyunImageAudit(auditImageBytesList, dto);
        if(!ret){
            return;
        }

        //获取所有的敏感词
        List<String> sensitiveWordsList = sensitiveService.getAll();

        log.info("敏感词审核：wmNewsId:{}", dto.getId());
        ret = sensitiveWordAudit(auditText, sensitiveWordsList, dto);
        if(!ret){
            return;
        }

        log.info("图片敏感词审核：wmNewsId:{}", dto.getId());
        ret = imageSensitiveWordAudit(auditImageBytesList, sensitiveWordsList, dto);
        if(!ret){
            return;
        }

        // 审核通过
        log.info("审核通过，wmNewsId:{}", dto.getId());

        Date publishTime = dto.getPublishTime();
        // 如果发布时间大于当前时间，则未来发布
        if(publishTime != null && publishTime.getTime() > System.currentTimeMillis()){
            // 延迟发布文章
            delayPublishWmNews(publishTime, wmUserId, dto);
            return;
        }

        // 把wmNews发布成article,feign
        Long articleId = articleFeignClient.createArticle(buildWmNewsResultDTO(wmUserId, dto));

        //回填article_id， 修改审核状态
        updateWmNewsInfo(dto.getId(), 9, articleId, "", true);
    }

    private void delayPublishWmNews(Date publishTime, Integer wmUserId, WmNewsDto dto){
        long delayMills = publishTime.getTime() - System.currentTimeMillis();
        WmNewsDelayTaskDTO taskDTO = new WmNewsDelayTaskDTO(dto.getId(), wmUserId);
        delayedQueueUtil.addQueue(JsonUtils.toString(taskDTO), delayMills, TimeUnit.MILLISECONDS, DELAY_QUEUE_NAME);
        // 更新状态为审核通过待发布
        updateWmNewsInfo(dto.getId(), 8, null, "");
    }

    @PostConstruct
    public void postConstruct(){
        takeTaskThreadPool.submit(new Runnable() {
            @Override
            public void run() {
                while(true){
                    try {
                        String taskJson = delayedQueueUtil.getDelayQueue(DELAY_QUEUE_NAME);
                        log.info("延迟任务出队，taskJson={}", taskJson);
                        // 业务逻辑，执行了24小时
                        bizThreadPool.submit(new Runnable() {
                            @Override
                            public void run() {
                                onReceiveDelayTask(taskJson);
                            }
                        });
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
    }

    /**
     * 延迟任务出队时候的回调
     * */
    private void onReceiveDelayTask(String taskJson){
        WmNewsDelayTaskDTO taskDTO = JsonUtils.toBean(taskJson, WmNewsDelayTaskDTO.class);
        WmNews newsDB = newsService.getById(taskDTO.getWmNewsId());
        if(newsDB == null){
            log.error("根据wmNewsId查询不到wmNews对象, wmNewsId={}", taskDTO.getWmNewsId());
            return;
        }
        WmNewsDto dto = BeanHelper.copyProperties(newsDB, WmNewsDto.class);
        // 把wmNews发布成article,feign
        Long articleId = articleFeignClient.createArticle(buildWmNewsResultDTO(taskDTO.getWmUserId(), dto));
        //回填article_id， 修改审核状态
        updateWmNewsInfo(taskDTO.getWmNewsId(), 9, articleId, "", true);
    }

    /**
     * 阿里云文本审核
     * */
    private boolean aliyunTextAudit(String auditText, WmNewsDto dto){
        try{
            Map<String, String> auditResultMap = textScan.greenTextScan(auditText);
            String auditResult = auditResultMap.get("suggestion");
            if(Objects.equals(auditResult, "pass")){
                // 通过
                return true;
            }else if(Objects.equals(auditResult, "review")){
                // 待人工
                String reason = auditResultMap.get("reason");
                updateWmNewsInfo(dto.getId(), 3, null, reason);
                return false;
            }else{
                //失败
                String reason = auditResultMap.get("reason");
                updateWmNewsInfo(dto.getId(), 2, null, reason);
                return false;
            }
        }catch (Exception e){
            log.error("阿里云文本审核异常:{}", ExceptionUtils.getStackTrace(e));
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
    }

    /**
     * 阿里云图片审核
     * */
    private boolean aliyunImageAudit(List<byte[]> imageBytesList, WmNewsDto dto){
        // 拿到图片，封面图 + 正文图
        if(CollectionUtils.isEmpty(imageBytesList)){
           return true;
        }
        try{
            Map<String, String> imageAuditResultMap = imageUploadScan.imageScan(imageBytesList);
            String auditResult = imageAuditResultMap.get("suggestion");
            if(Objects.equals(auditResult, "pass")){
                // 通过
                return true;
            }else if(Objects.equals(auditResult, "review")){
                // 待人工
                String reason = imageAuditResultMap.get("label");
                updateWmNewsInfo(dto.getId(), 3, null, reason);
                return false;
            }else{
                //失败
                String reason = imageAuditResultMap.get("label");
                updateWmNewsInfo(dto.getId(), 2, null, reason);
                return false;
            }
        }catch (Exception e){
            log.error("阿里云图片审核异常:{}", ExceptionUtils.getStackTrace(e));
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
    }

    /**
     * 自定义敏感词审核
     * */
    private boolean sensitiveWordAudit(String auditText, List<String> sensitiveWordsList, WmNewsDto dto){
        if(CollectionUtils.isEmpty(sensitiveWordsList)){
            return true;
        }
        Map<String, Integer> sensitiveAuditResultMap = SensitiveWordUtil.matchWords(sensitiveWordsList, auditText);
        if(CollectionUtils.isEmpty(sensitiveAuditResultMap)){
            // 通过
            return true;
        }else{
            String reason = sensitiveAuditResultMap.keySet().stream().collect(Collectors.joining(","));
            updateWmNewsInfo(dto.getId(), 2, null, reason);
            return false;
        }
    }

    /**
     * 自定义图片敏感词审核
     * */
    private boolean imageSensitiveWordAudit(List<byte[]> imageBytesList, List<String> sensitiveWordsList, WmNewsDto dto){
        // 拿图片，封面图 + 正文图
        if(CollectionUtils.isEmpty(imageBytesList) || CollectionUtils.isEmpty(sensitiveWordsList)) {
            return true;
        }
        try{
            for(byte[] imageBytes : imageBytesList){
                BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(imageBytes));
                String imageText = tess4jClient.doOCR(bufferedImage);
                Map<String, Integer> sensitiveAuditResultMap = SensitiveWordUtil.matchWords(sensitiveWordsList, imageText);
                if(CollectionUtils.isEmpty(sensitiveAuditResultMap)){
                    // 通过
                    continue;
                }else{
                    String reason = sensitiveAuditResultMap.keySet().stream().collect(Collectors.joining(","));
                    updateWmNewsInfo(dto.getId(), 2, null, reason);
                    return false;
                }
            }
        }catch(Exception e){
            log.error("图片敏感词审核异常:{}", ExceptionUtils.getStackTrace(e));
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
        return true;
    }


    private String buildAuditText(List<WmNewsContentItem> wmNewsContentItems, WmNewsDto dto){
        String title = dto.getTitle();
        String labels = dto.getLabels();
        String auditContent = wmNewsContentItems.stream()
                .filter(item-> Objects.equals(item.getType(), WmNewsContentItem.TEXT_TYPE))
                .map(item->item.getValue())
                .collect(Collectors.joining("-"));
        return title + "-" + auditContent +"-" + labels;
    }

    private List<byte[]> buildAuditImageBytesList(List<WmNewsContentItem> wmNewsContentItems, WmNewsDto dto){
        List<byte[]> imageBytesList = new ArrayList<>();
        // 封面图
        List<String> coverImageUrlList = dto.getImages();
        if (!CollectionUtils.isEmpty(coverImageUrlList)) {
            for (String coverUrl : coverImageUrlList) {
                byte[] imageBytes = minioService.downLoadFile(coverUrl);
                imageBytesList.add(imageBytes);
            }
        }
        // 正文图
        List<String> bodyImageUrlList = wmNewsContentItems.stream().filter(item -> Objects.equals(item.getType(), WmNewsContentItem.IMAGE_TYPE))
                .map(item -> item.getValue())
                .collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(bodyImageUrlList)) {
            for (String bodyUrl : bodyImageUrlList) {
                byte[] imageBytes = minioService.downLoadFile(bodyUrl);
                imageBytesList.add(imageBytes);
            }
        }
        return imageBytesList;
    }

    private WmNewsResultDTO buildWmNewsResultDTO(Integer wmUserId, WmNewsDto dto){
        WmNewsResultDTO wmNewsResultDTO = new WmNewsResultDTO();
        wmNewsResultDTO.setId(dto.getId());
        wmNewsResultDTO.setWmUserId(wmUserId);
        wmNewsResultDTO.setTitle(dto.getTitle());
        wmNewsResultDTO.setContent(dto.getContent());
        wmNewsResultDTO.setType(dto.getType());
        wmNewsResultDTO.setChannelId(dto.getChannelId());
        wmNewsResultDTO.setChannelName("ch_name");// TODO
        wmNewsResultDTO.setLabels(dto.getLabels());
        //如果没有发布时间，默认是当前时间
        wmNewsResultDTO.setPublishTime(dto.getPublishTime()==null?new Date():dto.getPublishTime());
        wmNewsResultDTO.setImages(CollectionUtils.isEmpty(dto.getImages())?null:dto.getImages().stream().collect(Collectors.joining(",")));
        return wmNewsResultDTO;
    }

    private void updateWmNewsInfo(Integer wmNewsId, Integer status, Long articleId, String reason){
        updateWmNewsInfo(wmNewsId, status, articleId, reason, null);
    }

    private void updateWmNewsInfo(Integer wmNewsId, Integer status, Long articleId, String reason, Boolean enable){
        WmNews updateWmNews = new WmNews();
        updateWmNews.setId(wmNewsId);
        updateWmNews.setStatus(status);
        if(articleId != null){
            updateWmNews.setArticleId(articleId);
        }
        if(enable != null){
            updateWmNews.setEnable(enable);
        }
        updateWmNews.setReason(reason);
        newsService.updateById(updateWmNews);
    }
}
